#include "p2d_force_attractor.h"

#include "dd/dd_man.h"
#include "script/scripthelper.h"
#include "util/log.h"		    // log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    // Needs to come in early


#include <algorithm>
#include <cassert>
using namespace part2d;


P2DForceAttractor::P2DForceAttractor()
{
    m_Offset    = math::Vec2(0,0);       // Position relative to group
    m_Type      = Constant;         // type of attraction 
    m_Magnitude = 1.0f;    // strength of attraction
}

P2DForceAttractor::~P2DForceAttractor()
{

}

P2DForce*	P2DForceAttractor::Clone()
{
	P2DForceAttractor*	pForce = new P2DForceAttractor();
	pForce->m_Offset	= m_Offset;
	pForce->m_Type		= m_Type;
	pForce->m_Magnitude	= m_Magnitude;
	return pForce;
}


bool    P2DForceAttractor::Load(LuaPlus::LuaObject& _LObj)
{
    m_Offset            = script::ScriptHelper::GetTableVec2(_LObj, "Offset");
    m_Magnitude         = script::ScriptHelper::GetTableFloat(_LObj, "Magnitude", 1.0f);
    std::string Type    = script::ScriptHelper::GetTableString(_LObj, "AttractorType", "Constant");

    std::transform(Type.begin(), Type.end(), Type.begin(), tolower);
    if (Type == "constant")     m_Type =  Constant; else
    if (Type == "linear")       m_Type =  Linear;   else
    if (Type == "invlinear")    m_Type =  InvLinear; else
    if (Type == "invsqr")       m_Type =  InvSqr;

    return true;
}

bool	P2DForceAttractor::RenderDebug(const math::Vec2& GroupCenter)
{
	dd::Manager::Instance().AddCircle2D(GroupCenter+ m_Offset, m_Magnitude*0.1f);
    return true;
}

bool	P2DForceAttractor::MoveParticles(const math::Vec2& GroupCenter, Particle2D* _Particles, unsigned int _usCount, float dt)
{
    switch (m_Type)
    {
        case Constant:      return MoveConst(GroupCenter, _Particles, _usCount, dt);
        case Linear:        return MoveLinear(GroupCenter, _Particles, _usCount, dt);
        case InvLinear:     return MoveInvLinear(GroupCenter, _Particles, _usCount, dt);
        case InvSqr:        return MoveInvSquare(GroupCenter, _Particles, _usCount, dt);
    }
    return true;
}


bool  P2DForceAttractor::MoveConst(const math::Vec2& P, Particle2D* _Particles, unsigned int _usCount, float dt)
{
    float Delta = dt * m_Magnitude;
	math::Vec2	Pos = P + m_Offset;
    for (unsigned int i=0; i<_usCount; i++)
    {
        Particle2D* pPart = &_Particles[i];
        if (pPart->Active)
        {
			math::Vec2	Dir = Pos - pPart->Pos;
			Dir.Normalize();

			math::Vec2	Speed = (pPart->Speed*pPart->Dir) + (Dir * Delta);
			pPart->Speed = Speed.Magnitude();
			pPart->Dir	 = Speed /pPart->Speed;	// normalize it again
        }
    }
    return true;
}

bool  P2DForceAttractor::MoveLinear(const math::Vec2& P, Particle2D* _Particles, unsigned int _usCount, float dt)
{
    float Delta = dt * m_Magnitude;
	math::Vec2	Pos = P + m_Offset;

    for (unsigned int i=0; i<_usCount; i++)
    {
        Particle2D* pPart = &_Particles[i];
        if (pPart->Active)
        {
			math::Vec2	Speed = (pPart->Speed*pPart->Dir) + ((Pos - pPart->Pos) * Delta);
			pPart->Speed = Speed.Magnitude();
			pPart->Dir	 = Speed /pPart->Speed;	// normalize it again
		}
    }
    return true;
}

bool  P2DForceAttractor::MoveInvLinear(const math::Vec2& P, Particle2D* _Particles, unsigned int _usCount, float dt)
{
    float Delta = dt * m_Magnitude;
	math::Vec2	Pos = P + m_Offset;
    for (unsigned int i=0; i<_usCount; i++)
    {
        Particle2D* pPart = &_Particles[i];
        if (pPart->Active)
        {
            math::Vec2  Dir = (Pos - pPart->Pos);
            float       InvMag = 1.0f / Dir.Magnitude();

			math::Vec2	Speed = (pPart->Speed*pPart->Dir) + ((Pos - pPart->Pos) * InvMag * InvMag * Delta);
			pPart->Speed = Speed.Magnitude();
			pPart->Dir	 = Speed /pPart->Speed;	// normalize it again
	
        }
    }
    return true;
}

bool  P2DForceAttractor::MoveInvSquare(const math::Vec2& P, Particle2D* _Particles, unsigned int _usCount, float dt)
{
    float Delta = dt * m_Magnitude;
	math::Vec2	Pos = P + m_Offset;
    for (unsigned int i=0; i<_usCount; i++)
    {
        Particle2D* pPart = &_Particles[i];
        if (pPart->Active)
        {
            math::Vec2  Dir = (Pos - pPart->Pos);
            float       InvMag = 1.0f / Dir.Magnitude();

			math::Vec2	Speed = (pPart->Speed*pPart->Dir) + ((Pos - pPart->Pos) * InvMag * InvMag * InvMag * Delta);
			pPart->Speed = Speed.Magnitude();
			pPart->Dir	 = Speed /pPart->Speed;	// normalize it again
		
		}
    }
    return true;
}
